﻿/**************************************************************
 *
 * 唯一标识：b784ad98-b7c2-43a2-b4bf-70acd32af6b6
 * 命名空间：Sgr.OrganizationAggregate
 * 创建时间：2023/8/3 10:07:10
 * 机器名称：DESKTOP-S0D075D
 * 创建者：antho
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：
 *
 **************************************************************/

using Sgr.Domain.Entities;
using Sgr.Domain.Managers;
using Sgr.Exceptions;
using Sgr.Generator;
using Sgr.UPMS.Domain.Departments;
using Sgr.UPMS.Domain.Users;
using Sgr.Utilities;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Sgr.UPMS.Domain.Organizations
{
    public class OrganizationManage : TreeNodeManageBase<Organization, long>, IOrganizationManage
    {
        private readonly ISegmentNumIdGenerator _numberIdGenerator;
        private readonly IOrganizationInvitationRepository _invitationRepository;

        private IOrganizationRepository _organizationRepository => (IOrganizationRepository)_repository;  // 转换父类引用

        public OrganizationManage(
            IOrganizationRepository organizationRepository,
            IOrganizationInvitationRepository invitationRepository,
            ISegmentNumIdGenerator numberIdGenerator)
            : base(organizationRepository)
        {
            _invitationRepository = invitationRepository ?? throw new ArgumentNullException(nameof(invitationRepository));
            _numberIdGenerator = numberIdGenerator ?? throw new ArgumentNullException(nameof(numberIdGenerator));
        }

        public async Task<Organization> CreateNewAsync(
            long? newId,
            string name,
            string staffSizeCode,
            string? remarks,
            long? parentId,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrWhiteSpace(name, nameof(name));

            //获取Id及Id-Path
            long id = newId ?? await _numberIdGenerator.GetUniqueIdAsync(UpmsConstant.DEFAULT_SEGMENT_KEY, cancellationToken);

            // 处理父级节点
            parentId ??= 0;

            Organization? parentNode = null;
            if (parentId > 0)
            {
                parentNode = await _organizationRepository.GetAsync(parentId.Value, cancellationToken);
                if (parentNode == null)
                    throw DomainException.ValidationError($"上级节点ID {parentId} 不存在");
            }

            string nodePath = GetNodePath(parentNode, id, 5);

            // 组织机构识别码 [随机生成]
            string identificationCode = await GenerateIdentificationCodeAsync(5, 3, cancellationToken);

            var entity = new Organization(identificationCode, name, staffSizeCode, remarks)
            {
                Id = id
            };

            entity.SetParent(parentId.Value, nodePath);
            return entity;
        }

        /// <summary>
        /// 创建邀请链接
        /// </summary>
        /// <param name="organizationId">组织标识</param>
        /// <param name="validityPeriod">有效期限</param>
        /// <param name="usageLimit">可用次数</param>
        /// <param name="description">描述信息</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns></returns>
        public async Task<OrganizationInvitation> CreateInvitationAsync(
            long organizationId,
            TimeSpan validityPeriod,
            int usageLimit = -1,
            string? description = null,
            CancellationToken cancellationToken = default)
        {
            if (usageLimit < -1)
                throw DomainException.ValidationError($"可用次数不能小于-1");

            // 验证组织是否存在
            _ = await _organizationRepository.GetAsync(organizationId, cancellationToken)
                ?? throw DomainException.NotFoundError($"组织机构[{organizationId}]不存在");

            // 生成邀请码
            string invitationCode = await GenerateInvitationCodeAsync();

            // 创建邀请记录
            var invitation = new OrganizationInvitation(
                organizationId,
                invitationCode,
                DateTime.UtcNow.Add(validityPeriod),
                usageLimit,
                description);

            return invitation;
        }

        /// <summary>
        /// 验证邀请码
        /// </summary>
        /// <param name="invitationCode">邀请码</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns></returns>
        public async Task<InvitationValidationResult> ValidateInvitationAsync(
            string invitationCode,
            CancellationToken cancellationToken = default)
        {
            var invitation = await _invitationRepository.GetByInvitationCodeAsync(
                invitationCode,
                cancellationToken);

            return new InvitationValidationResult(invitation);
        }

        /// <summary>
        /// 提交组织认证信息
        /// </summary>
        /// <param name="organization">组织实体</param>
        /// <param name="usci">统一社会信用代码</param>
        /// <param name="businessLicensePath">营业执照文件路径</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>异步任务</returns>
        /// <exception cref="DomainException">
        ///     当发生以下情况时抛出：
        ///     - 统一社会信用代码已存在
        ///     - 组织状态不允许提交认证
        ///     - 组织已完成认证
        /// </exception>
        public async Task SubmitToConfirmed(Organization organization, string usci, string businessLicensePath, CancellationToken cancellationToken = default)
        {
            Check.NotNull(organization, nameof(organization));
            Check.StringNotNullOrEmpty(usci, nameof(usci));
            Check.StringNotNullOrEmpty(businessLicensePath, nameof(businessLicensePath));

            // 业务规则验证
            if (organization.State != EntityStates.Normal)
            {
                throw DomainException.ValidationError(
                    $"组织机构状态为 {organization.State}，不允许提交认证信息");
            }

            if (organization.Confirmed != ConfirmedStates.WaitingConfirmed)
            {
                throw DomainException.ValidationError(
                    $"组织机构认证状态为 {organization.Confirmed}，不允许重复提交认证信息");
            }

            // 检查统一社会信用代码唯一性
            if (!await _organizationRepository.CodeIsUniqueAsync(
                usci,
                organization.Id,
                cancellationToken))
            {
                throw DomainException.ValidationError(
                    $"统一社会信用代码 {usci} 已被其他组织使用");
            }

            // 更新认证信息
            organization.Code = usci;
            organization.BusinessLicensePath = businessLicensePath;
            organization.Confirmed = ConfirmedStates.WaitingReview;
        }

        /// <summary>
        /// 组织注销前审查
        /// </summary>
        /// <param name="organization"></param>
        /// <param name="operatorUser"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<DomainRuleCheckResult> CancellationExamination(Organization organization, User operatorUser, CancellationToken cancellationToken = default)
        {
            Check.NotNull(organization, nameof(organization));
            Check.NotNull(operatorUser, nameof(operatorUser));

            //  Constant.SUPER_ADMIN_ORG_ID 对应的组织不可以注销
            if (organization.Id == Constant.SUPER_ADMIN_ORG_ID)
            {
                return DomainRuleCheckResult.Fail("系统默认的运维组织不允许注销!");
            }

            // 仅当本组织的超管 或者 Constant.SUPER_ADMIN_ORG_ID 组织下的用户方可执行注销操作
            if (!((operatorUser.IsSuperAdmin && operatorUser.OrgId == organization.Id)
                || operatorUser.OrgId == Constant.SUPER_ADMIN_ORG_ID))
            {
                return DomainRuleCheckResult.Fail("仅能由本组织的超级管理员执行注销操作!");
            }

            if (await this._repository.HasChildNodesAsync(organization, cancellationToken))
                return DomainRuleCheckResult.Fail("当前组织有下级组织，不允许注销!");

            return DomainRuleCheckResult.Ok();
        }

        public override Task<bool> RemoveNodeAsync(long id, bool isCascade = true, CancellationToken cancellationToken = default)
        {
            throw DomainException.LimitExceededError($"不被允许的操作");
        }

        public override Task<bool> RemoveNodeAsync(Organization entity, bool isCascade = true, CancellationToken cancellationToken = default)
        {
            throw DomainException.LimitExceededError($"不被允许的操作");
        }

        /// <summary>
        /// 删除某个节点前的操作
        /// </summary>
        /// <param name="entity"></param>
        protected override void BeforeDeleteEntity(Organization entity)
        {
            base.BeforeDeleteEntity(entity);
        }

        /// <summary>
        /// 修改树节点的父节点
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="parentEntity"></param>
        /// <param name="maxLevel"></param>
        protected override void ChangeEntityParent(Organization entity, Organization? parentEntity, int maxLevel = 5)
        {
            var parentId = parentEntity?.Id ?? 0;

            string path = GetNodePath(parentEntity, entity.Id, maxLevel);

            entity.SetParent(parentId, path);
        }

        #region 生成组织机构识别码

        /// <summary>
        /// 批量生成候选识别码
        /// </summary>
        /// <param name="count">生成数量</param>
        /// <returns>候选识别码列表</returns>
        private static List<string> GenerateCandidateIdentificationCodes(int count)
        {
            // 定义可用字符集（排除0和O）
            const string allowedChars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"; // 排除易混淆字符

            var candidates = new HashSet<string>();

            while (candidates.Count < count)
            {
                // 生成6位随机码
                char[] code = new char[6];
                for (int i = 0; i < 6; i++)
                {
                    code[i] = allowedChars[RandomHelper.GetRandom(allowedChars.Length)];
                }

                candidates.Add(new string(code));
            }

            return candidates.ToList();
        }

        /// <summary>
        /// 生成有效的识别码
        /// </summary>
        /// <param name="batchSize">每批生成的候选码数量</param>
        /// <param name="maxAttempts">最大重试次数</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>有效的识别码</returns>
        private async Task<string> GenerateIdentificationCodeAsync(
            int batchSize = 10,
            int maxAttempts = 3,
            CancellationToken cancellationToken = default)
        {
            string result = "";
            for (int attempt = 0; attempt < maxAttempts; attempt++)
            {
                // 生成一批候选识别码
                List<string> candidates = GenerateCandidateIdentificationCodes(batchSize);

                // 批量检查已存在的识别码
                var existingCodes = await _organizationRepository.GetExistingIdentificationCodesAsync(
                    candidates,
                    cancellationToken);

                // 找出第一个未使用的识别码
                var availableCode = candidates.FirstOrDefault(c => !existingCodes.Contains(c));
                if (availableCode != null)
                {
                    result = availableCode;
                    break;
                }
            }

            if (string.IsNullOrEmpty(result))
            {
                throw DomainException.LimitExceededError("无法生成有效的组织机构识别码");
            }

            return result;
        }

        /// <summary>
        /// 生成邀请码
        /// </summary>
        /// <returns></returns>
        private static Task<string> GenerateInvitationCodeAsync()
            => Task.FromResult(Guid.NewGuid().ToString("N"));

        #endregion 生成组织机构识别码
    }
}